/*
 * HTTPRequest.c
 *      Author: DePhille
 */


#include "HTTPRequest.h"


/**
 * Creates a new HTTPRequest.
 *
 * @return The new HTTPRequest or NULL if creation failed.
 */
static PC_HTTPRequest* PC_HTTPRequest_New() {
	PC_HTTPRequest* newRequest; // Clean-up: No

	// Make new request:
	newRequest = g_new(PC_HTTPRequest, 1);
	if (!newRequest) {
		purple_debug(PURPLE_DEBUG_ERROR, "PidginCote", "PC_HTTPRequest: Unable to make new request struct.\r\n");
		return NULL;
	}

	// Fill in default values:
	newRequest->destination = g_strdup(PC_NET_SERVER);
	newRequest->payload = NULL;
	newRequest->useHTTP11 = FALSE;
	newRequest->userAgent = g_strdup(PC_NET_USERAGENT);
	newRequest->method = PC_HTTPREQUEST_GET;
	newRequest->uri = NULL;
	newRequest->contentType = g_strdup(PC_HTTPREQUEST_CONTENTTYPE);
	newRequest->customHeaders = NULL;
	newRequest->token = g_strdup(pc_net_token);
	newRequest->timeOut = PC_HTTPREQUEST_TIMEOUT;
	newRequest->maxRetries = PC_HTTPREQUEST_MAXRETRIES;
	newRequest->callBack = NULL;
	newRequest->callBackData = NULL;
	newRequest->retries = 0;
	newRequest->timeSent = 0;
	newRequest->urlData = NULL;

	// Return struct:
	return newRequest;
}



/**
 * Frees a HTTPRequest from memory.
 *
 * @param request The HTTPRequest to be removed.
 */
static void PC_HTTPRequest_Delete(PC_HTTPRequest* request) {
	if (request) {
		// Free memory:
		g_free(request->destination);
		g_free(request->userAgent);
		g_free(request->payload);
		g_free(request->uri);
		g_free(request->contentType);
		g_list_free(request->customHeaders);
		g_free(request->token);

		// Free entire object:
		g_free(request);
	}

	// Display some debug info:
}



/**
 * Deletes a custom HTTP header from the HTTPRequest.
 *
 * @param request		The request.
 * @param headerName	Name of the custom HTTP header to be removed.
 */
static void PC_HTTPRequest_delCustomHeader(PC_HTTPRequest* request, const gchar* headerName) {
	GList* 		curHeader;	// Clean-up: No
	gchar*		header;		// Clean-up: No

	// Only accept valid requestObj:
	if (!request) {
		purple_debug_warning("PidginCote", "PC_HTTPRequest_delCustomHeader: Invalid request passed.\r\n");
		return;
	}

	// Check if headerName was given:
	if (!headerName) {
		purple_debug_warning("PidginCote", "PC_HTTPRequest_delCustomHeader: No headerName passed.\r\n");
		return;
	}

	// Iterate over the custom headers:
	curHeader = request->customHeaders;
	while (curHeader) {
		// Check if element is a valid string:
		header = (gchar*) curHeader->data;
		if (!header) {
			purple_debug_warning("PidginCote", "PC_HTTPRequest_delCustomHeader: Invalid element detected in customHeaders.\r\n");
		} else {

			// Compare headerName:
			if (PC_Util_Strncmp0(header, headerName, strlen(headerName)) == 0) {
				request->customHeaders = g_list_remove(request->customHeaders, curHeader->data);
				return;
			}
		}

		// Next element:
		curHeader = g_list_next(curHeader);
	}

	purple_debug_info("PidginCote", "PC_HTTPRequest_delCustomHeader: No header deleted, header %s not found.\r\n", headerName);
}



/**
 * Adds a custom header to the given HTTPRequest.
 *
 * @param request		The request.
 * @param headerName	The name of the HTTP header to be added.
 * @param headerValue	The value for the new HTTP header.
 *
 * TODO: Fix error with delCustomHeader
 */
static void PC_HTTPRequest_addCustomHeader(PC_HTTPRequest* request, const gchar* headerName, const gchar* headerValue) {
	gchar*	header;		// Clean-up: No


	// Only accept valid requestObj:
	if (!request) {
		purple_debug_warning("PidginCote", "PC_HTTPRequest_addCustomHeader: Invalid request passed.\r\n");
		return;
	}

	// Check if headerName was given:
	if (!headerName) {
		purple_debug_warning("PidginCote", "PC_HTTPRequest_addCustomHeader: No headerName passed.\r\n");
		return;
	}

	// Check if headerValue was given:
	if (!headerValue) {
		purple_debug_warning("PidginCote", "PC_HTTPRequest_addCustomHeader: No headerValue passed.\r\n");
		return;
	}

	// Delete the header first (if it exists):
	//PC_HTTPRequest_delCustomHeader(request, headerName);

	// Build header:
	header = g_strdup_printf("%s: %s", headerName, headerValue);
	if (!header) {
		purple_debug_error("PidginCote", "PC_HTTPRequest_addCustomHeader: Unable to build header.\r\n");
		return;
	}

	// Add it to the list:
	request->customHeaders = g_list_prepend(request->customHeaders, header);
}


/**
 * Sets a callBack for the given request.
 * 	This callBack will be called upon completion of the request along with the callBackData if set.
 * 	The caller is responsible for cleaning up the callBackData in the callBack.
 *
 * @param request		The request.
 * @param callBack		The callback to be called if a response to the request is received. (Can be NULL)
 * @param callBackData	The data to be passed to the callback. (Can be NULL)
 */
static void PC_HTTPRequest_SetCallBack(PC_HTTPRequest* request, PC_Net_HTTP_CallBack callBack, gpointer callBackData) {
	// Check input:
	if (!request) {
		purple_debug_warning("PidginCote", "PC_HTTPRequest_SetCallBack: Missing 'request' parameter.\r\n");
		return;
	}

	// Set values:
	request->callBack = callBack;
	request->callBackData = callBackData;
}

/**
 * Converts a request object to a string.
 *
 * @param	request	The request.
 * @return	The request as a string or NULL if failed.
 */
static gchar* PC_HTTPRequest_ToString(const PC_HTTPRequest* request) {
	gchar*			methodStr;				// Clean-up: No
	gchar*			httpVer;				// Clean-up: No
	gchar*			customHeaders;			// Clean-up: Yes
	GList*			curHeader;				// Clean-up: No
	gchar*			curHeaderStr;			// Clean-up: No
	gchar*			tmpStr;					// Clean-up: Yes
	gchar*			content;				// Clean-up: No
	unsigned int	contentLength;			// Clean-up: No
	gchar*			requestStr;				// Clean-up: No
	gchar*			authorizationHeader;	// Clean-up: Yes

	// Check input:
	if (!request) {
		purple_debug_warning("PidginCote", "PC_HTTPRequest_ToString: Missing 'request' parameter.\r\n");
		return NULL;
	} else if (!request->destination) {
		purple_debug_error("PidginCote", "PC_HTTPRequest_ToString: request->destination not set.\r\n");
		return NULL;
	} else if (!request->uri) {
		purple_debug_error("PidginCote", "PC_HTTPRequest_ToString: request->uri not set.\r\n");
		return NULL;
	} else if (!request->userAgent) {
		purple_debug_error("PidginCote", "PC_HTTPRequest_ToString: request->userAgent not set.\r\n");
		return NULL;
	} else if (!request->contentType) {
		purple_debug_error("PidginCote", "PC_HTTPRequest_ToString: request->contentType not set.\r\n");
		return NULL;
	}

	// Determine contentLength and content:
	content = "";
	contentLength = 0;
	if (request->payload) {
		content = request->payload;
		contentLength = strlen(content);
	}

	// Determine Authorization header:
	if (request->token) {
		authorizationHeader = g_strdup_printf("PCToken: %s\r\n", request->token);
	} else {
		authorizationHeader = g_strdup("");
	}

	// Determine method:
	methodStr = "GET";
	if (request->method == PC_HTTPREQUEST_POST) {
		methodStr = "POST";
	}

	// Determine HTTP Version:
	httpVer = "HTTP/1.0";
	if (request->useHTTP11) {
		httpVer = "HTTP/1.1";
	}

	// Build custom headers:
	customHeaders = g_strdup("");
	curHeader = request->customHeaders;
	while (curHeader) {
		// Store current headers
		tmpStr = customHeaders;

		// Fetch new header:
		curHeaderStr = (gchar*) curHeader->data;
		if (!curHeaderStr) {
			purple_debug_warning("PidginCote", "PC_Net_SendRequest: Invalid element detected in customHeaders.\r\n");
		} else {
			// Add header:
			customHeaders = g_strdup_printf("%s%s\r\n", customHeaders, curHeaderStr);

			// Delete old headerStr:
			g_free(tmpStr);
			tmpStr = NULL;
		}

		// Next element:
		curHeader = g_list_next(curHeader);
	}

	// Build request:
	requestStr = g_strdup_printf(
			"%s %s %s\r\n"
			"User-Agent: %s\r\n"
			"Host: %s\r\n"
			"Content-type: %s\r\n"
			"Content-Length: %d\r\n"
			"%s"
			"%s"
			"\r\n"
			"%s",
			methodStr,
			request->uri,
			httpVer,
			request->userAgent,
			request->destination,
			request->contentType,
			contentLength,
			authorizationHeader,
			customHeaders,
			content);

	// Clean-up:
	g_free(customHeaders);
	g_free(authorizationHeader);

	// Return:
	return requestStr;
}

/**
 * Sets the time after which the request should time out.
 *
 * @param timeOut	The timeOut time in seconds.
 */
static void PC_HTTPRequest_SetTimeOut(PC_HTTPRequest* request, unsigned short timeOut) {
	// Check input:
	if (!request) {
		purple_debug_warning("PidginCote", "PC_HTTPRequest_SetTimeOut: Missing 'request' parameter.\r\n");
		return;
	}

	// Set value:
	request->timeOut = timeOut;
}

/**
 * Sets the maximum amount of retries for a request.
 *
 * @param	maxRetries	The maximum amount of retries for this request.
 */
static void PC_HTTPRequest_SetMaxRetries(PC_HTTPRequest* request, unsigned short maxRetries) {
	// Check input:
	if (!request) {
		purple_debug_warning("PidginCote", "PC_HTTPRequest_SetMaxRetries: Missing 'request' parameter.\r\n");
		return;
	}

	// Set value:
	request->maxRetries = maxRetries;
}
